﻿#include <assert.h>
#include <iostream>
#include <sstream>
#include "Engine.h"
#include "Move.h"
#include "Types.h"
#include "Platform.h"
#include "Thread.h"
#include <vector>
#include <list>
using namespace std;

Engine ChessEngine;
//long instanceCounter; // for perf debugging

// extern ChessEngine isntance = the dummy cstr is replaced by the init() function
Engine::Engine()
{
}

void Engine::init()
{
	debugMode = true;

	/*
	// Transposition table
	TT = new TranspositionTable();
	useTT = true;
	*/

	// new alphabeta implementation
	//moves = new vector<Move>();
	//scores = new vector<MoveStack>();

	// Watch (timer)
	//watch = new Watch();
	isSearchMove = false;

	// Smart timing (default = true)
	isSmartTiming = false;

	this->brainStatus = PAUSED;
	//this->currentPosition = new Position();

	// TOD0 debug init = Blitz 2'+6"/ply - to be removed
	this->SetWtime(120000);
	this->SetWinc(6000);
	this->SetBtime(120000);
	this->SetBinc(6000);
	SetStartPosition();

	//mainThread = new Thread(new ThreadStart(this->BrainMainThreadLoop));
	//clockThread = new Thread(new ThreadStart(this->ClockThreadLoop));

	//Thread mainThread(&BrainMainThreadLoop);
	//Thread clockThread(&ClockThreadLoop);

	//thread_create(handle, &BrainMainThreadLoop, mainThread);
	//thread_create(handle, &ClockThreadLoop, clockThread);

	// Launching threads
	//mainThread.start_fn();
	//clockThread.start_fn();

	//Threads.ptEngine = this;
	moves.reserve(256);
}

void Engine::Play(Move m){
	currentPosition.Play(m);
}

void Engine::Quit()
{
	// Delete the 2 main threads
	// mainThread.Abort();
	// clockThread.Abort();
	brainStatus = QUITTING;
}

/*
// This thread function manage the change of status of the brain
// When searching we only need to check the brainStatus
// (time elapsed is available via the watch variable)
void Engine::ClockThreadLoop()
{
while (true)
{
while (brainStatus != THINKING)
{
cout << "WAITING..." << endl;
Sleep(100);
//Thread.Sleep(100);
}

if(brainMode == MOVETIME_MODE
|| brainMode == ADJUSTTIME_MODE)
{
// the search can be manually stopped via the command line
// so a Sleep(thinkDelay) is not accurate enough
// we need to check regularly if the brain was not stop by another way
while (watch.GetElapsedTime() < thinkDelay
&& brainStatus != PAUSED)
{
Sleep(100);
//Thread.Sleep(100);
} 
brainStatus = PAUSED;
}
}
}
*/

/*
// Main thread - launch the different search modes
void Engine::BrainMainThreadLoop()
{
// TODO: make QUITTING cleaner
while (brainStatus != QUITTING)
{
// wait loop
while ((brainStatus != THINKING) 
&& (brainStatus != QUITTING))
{
Sleep(100);
//cout << "waiting..." << endl;
//Thread.Sleep(100);
}

// Thread main task

// global value
bestMove = MOVE_NULL;
bestScore = 0;
int depth;
ReturnCode searchResult = OK;
switch (brainMode)
{
case ADJUSTTIME_MODE:
case MOVETIME_MODE:

depth = 1;
while (searchResult == OK
&& brainStatus == THINKING)
{
searchResult = Search(currentPosition, depth);
depth++;
}

break;
case MATE_MODE:
case DEPTH_MODE:
depth = 1;
while (brainStatus == THINKING && depth <= depthLevel)
{
Search(currentPosition, depth);
depth++;
}
break;
case NODES_MODE:
depth = 1;
while (brainStatus == THINKING && nbProcessedNodes < nodeMaxNumber)
{
Search(currentPosition, depth);
depth++;
}
break;
case INFINITE_MODE:
depth = 1;
while (brainStatus == THINKING)
{
Search(currentPosition, depth);
depth++;
}
break;
default:
brainStatus = PAUSED;
break;
}
this->Stop();

if (bestMove != MOVE_NULL)
{
// Display best move
cout << "bestmove " <<  move_to_string(bestMove) << endl;
}
else
{
// Display no move
cout << "bestmove -" << endl;
}

}
}
*/

ReturnCode Engine::Search(Position& pos, int depth)
{
	//cout << "SEARCH LAUNCHED at depth " << depth << endl;
	// TODO : global variables should be declared as local for multithreading
	selectiveDepth = 0;
	//Position pos(position);
	ReturnCode returnCode = KO;

	int alpha = -VALUE_INFINITE;
	int beta = VALUE_INFINITE;

	scores.clear();
	// search for each move
	//currentTurn = pos.GetTurn();
	nbProcessedNodes = moves.size(); // should be initialized outside (this method can be launched several times)
	//foreach (Move currmove in moves)
	

	for(vector<Move>::iterator currmove = moves.begin(); currmove != moves.end(); currmove++)
		//for(int i=0; i<moves.size(); i++)
	{
		//Move currmove = moves.get(i);

		pos.Play(*currmove);
		// Next ply is for the opponent
		int score = 0;
		//// TODO remove iterative statement FOR DEBUG ONLY ******************
		////if (debugNegmax)
		////{
		////	// WORKING
		////	returnCode = AlphaBetaNegmaxMin(score, pos, alpha, beta, 1, depth);

		////}
		////else if (debugIterativeAB)
		////	returnCode = AlphaBetaIterative(score, pos, alpha, beta, 1, depth);
		////else if (debugNegascout)
		////{
		////	returnCode = NegaScout(score, pos, alpha, beta, 1, depth);
		////	if (((currentTurn + depth) % 2) == 0)
		////		score *= -1;
		////}
		////else
		//returnCode = AlphaBetaMin(score, pos, alpha, beta, 1, depth);
		Move* pv = new Move[256]; // should be delete when leaving the function scope
		pv[0] = *currmove;
		returnCode = AlphaBetaMin(score, pos, alpha, beta, 1, depth, pv);
		////returnCode = AlphaBetaMinMax(score, ref pos, ref pv, alpha, beta, 1, depth, false);

		pos.Unplay(*currmove);
		if (returnCode != OK)
			break;
		//MoveStack movescore = {*currmove,score}; // = new Tuple<Move, int, Stack<Move>>(currmove, score, pv);
		MoveScorePv movescore = {*currmove,score,pv}; // = new Tuple<Move, int, Stack<Move>>(currmove, score, pv);
		scores.push_back(movescore);
	}

	// sort move by score
	if (returnCode == OK)
	{
		// Update only if search was fully performed
		Sort(scores);
		bestMove = (scores[0].move);
		bestScore = scores[0].score;
		bestPv = scores[0].pv;
		DisplayBestPv(depth, nbProcessedNodes, watch.GetElapsedTime(), bestPv);
		if(debugMode)
			DisplayScores(scores, depth);
	}
	return returnCode;
}

ReturnCode Engine::AlphaBetaMinMax(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth, bool maxPlayer)
{
	if (brainStatus != THINKING)
		return STOPTHINKING; // quick exit
	//string pfen = pos.GetPseudoFEN();
	if (depth == maxDepth)
	{
		score = EvaluateRelative(pos, this->GetTurn());
		return OK;
	}
	vector<Move> moves;// = new vector<Move>();
	GetLegalMoves(pos, moves);


	if(maxPlayer){
		if (moves.size() == 0)
			if (IsCheck(pos))
			{
				score = -VALUE_MATE + depth;
				return OK;
			}
			else
			{
				score = VALUE_DRAW;
				return OK;
			}
			nbProcessedNodes += moves.size();
			//foreach (Move m in moves)
			for(vector<Move>::iterator m = moves.begin(); m != moves.end(); m++)
				//for(int i = 0; i< moves.size(); i++)
			{
				//Move m = moves.get(i);

				pos.Play(*m);
				ReturnCode returnCode = AlphaBetaMinMax(score, pos, alpha, beta, depth + 1, maxDepth,!maxPlayer);
				pos.Unplay(*m);
				if (returnCode != OK)
					return returnCode;
				if (score >= beta)
				{
					score = beta;   // fail hard beta-cutoff
					return OK;
				}
				if (score > alpha)
				{
					alpha = score; // alpha acts like max in MiniMax
				}
			}
			score = alpha;
			return OK;
	}
	else
	{
		if (moves.size() == 0)
			if (IsCheck(pos))
			{
				score = VALUE_MATE - depth;
				return OK;
			}
			else
			{
				score = VALUE_DRAW;
				return OK;
			}
			nbProcessedNodes += moves.size();
			//TranspTableEntry tte = new TranspTableEntry();
			//foreach (Move m in moves)
			for(vector<Move>::iterator m = moves.begin(); m != moves.end(); m++)
				//for(int i = 0; i< moves.size(); i++)
			{
				//Move m = moves.get(i);

				pos.Play(*m);
				ReturnCode returnCode = AlphaBetaMinMax(score, pos, alpha, beta, depth + 1, maxDepth, !maxPlayer);
				pos.Unplay(*m);
				if (returnCode != OK)
					return returnCode;
				if (score <= alpha)
				{
					/*// TT part
					if (useTT)
					{
					tte.nodeType = NodeType.ALL_NODE; //alpha-cutoff
					tte.score = alpha;
					tte.bestMove = m;
					TT.Add(pos.GetPseudoFEN(), tte);
					}*/
					score = alpha; // fail hard alpha-cutoff
					return OK;
				}
				if (score < beta)
				{
					/*if (useTT)
					{
					tte.score = beta;
					tte.bestMove = m;
					}*/
					beta = score; // beta acts like min in MiniMax
				}
			}
			/*if (useTT)
			{
			tte.nodeType = NodeType.PV_NODE; //in bounds score
			//tte.score = beta; //already stored
			//tte.bestMove = m;
			TT.Add(pos.GetPseudoFEN(), tte);
			}*/
			score = beta;
			return OK;
	}

}

void Engine::DisplayScores(vector<MoveStack>& scores, int depth)
{
	int MOVES_PER_LINE = 5;
	cout << "NEW MOVES EVALUATION -------------------- depth " << depth << endl;
	int mvCount = 0;
	//foreach (Tuple<Move, int, Stack<Move>> mscore in scores)
	for(vector<MoveStack>::iterator mscore = scores.begin(); mscore != scores.end(); mscore++)
		//for(int i = 0; i< moves.size(); i++)
	{

		Move move = (*mscore).move;
		int score = (*mscore).score;
		cout << move_to_string(move) << "=";
		if(score < -(VALUE_MATE / 2) || score > (VALUE_MATE / 2))
			cout << ScoreToString(score) << " ";
		else // no mate
			cout << score << " ";

		if ((mvCount + 1) % MOVES_PER_LINE == 0) 
			cout << endl ;
		mvCount++;
	}
	cout << endl ;
	cout << "-------------------------------------------------" << endl;
}

void Engine::DisplayScores(vector<MoveScorePv>& scores, int depth)
{
	int MOVES_PER_LINE = 5;
	cout << "NEW MOVES EVALUATION -------------------- depth " << depth << endl;
	int mvCount = 0;
	//foreach (Tuple<Move, int, Stack<Move>> mscore in scores)
	for(vector<MoveScorePv>::iterator mscore = scores.begin(); mscore != scores.end(); mscore++)
		//for(int i = 0; i< moves.size(); i++)
	{

		Move move = (*mscore).move;
		int score = (*mscore).score;
		cout << move_to_string(move) << "=";
		if(score < -(VALUE_MATE / 2) || score > (VALUE_MATE / 2))
			cout << ScoreToString(score) << " ";
		else // no mate
			cout << score << " ";

		if ((mvCount + 1) % MOVES_PER_LINE == 0) 
			cout << endl ;
		mvCount++;
	}
	cout << endl ;
	cout << "-------------------------------------------------" << endl;
}


// Sort moves depending on the final score
// Bubble sort - can be improved
// TODO: improve the sort method
void Engine::Sort(vector<MoveStack> & scores)
{
	int i;
	int j;
	MoveStack temp;
	int x = scores.size();

	for (i = (x - 1); i >= 0; i--)
	{
		for (j = 1; j <= i; j++)
		{
			if (scores[j - 1].score < scores[j].score)
			{
				temp = scores[j - 1];
				scores[j - 1] = scores[j];
				scores[j] = temp;
			}
		}
	}
}

void Engine::Sort(vector<MoveScorePv> & scores)
{
	int i;
	int j;
	MoveScorePv temp;
	int x = scores.size();

	for (i = (x - 1); i >= 0; i--)
	{
		for (j = 1; j <= i; j++)
		{
			if (scores[j - 1].score < scores[j].score)
			{
				temp = scores[j - 1];
				scores[j - 1] = scores[j];
				scores[j] = temp;
			}
		}
	}
}

// equivalent to (m1 < m2)
//bool Engine::compare_potential_value(MoveStack m1, MoveStack m2){
bool compare_potential_value(MoveStack m1, MoveStack m2){

	return m1.score < m2.score;
}

// Sort moves depending on their potential respective values (before evaluation)
// Bubble sort - can be improved
// use of potential_value(b, moves[j]) can be avoid
// TODO: improve the sort method
void Engine::Sort(Board & b, vector<Move> & moves)
{
	/*
	list<MoveStack> moveList;
	for(unsigned int i=0;i<moves.size();i++){
		MoveStack ms;
		ms.move = moves[i];
		ms.score= potential_value(b,moves[i]);
		//moveList.push_back(MoveStack( { moves[i] , potential_value(b,moves[i]) });
		moveList.push_back(ms);
	}
	moveList.sort(compare_potential_value);
	int index=0;
	for(list<MoveStack>::iterator ms = moveList.begin(); ms != moveList.end(); ms++)
	{
		moves[index++] = ms->move;
	}
	*/


	int i;
	int j;
	int x = moves.size();
	Move temp;
 
	for (i = (x - 1); i >= 0; i--)
	{
		for (j = 1; j <= i; j++)
		{
			//if (potential_value(moves.get(j - 1)) < potential_value(moves.get(j)))
			if (potential_value(b, moves[j - 1]) < potential_value(b, moves[j]))
			{
				//moves.swap(j - 1,j);
				temp = moves[j - 1];
				moves[j - 1] = moves[j];
				moves[j] = temp;
			}
		}
	}
}



inline int GetVal(Piece piece)
{
    switch (piece)
    {
        case W_KING:
        case B_KING:
            return 1900;
        case W_QUEEN:
        case B_QUEEN:
            return 900;
        case W_ROOK:
        case B_ROOK:
            return 500;
        case W_BISHOP:
        case B_BISHOP:
            return 300;
        case W_KNIGHT:
        case B_KNIGHT:
            return 300;
        case W_PAWN:
        case B_PAWN:
            return 90;
        default:
            return 0;
    }
}

// TODO: use a quick eval function with shifts
int Engine::potential_value(Board & b, Move m)
{
    //int captureEval = 2048*GetVal(Board_GetPiece(b, to_sq(m)));
    int captureEval = GetVal(Board_GetPiece(b, to_sq(m))) << 11;
	// lowest piece moved for capture first
    int pieceEval = 2048 - GetVal(Board_GetPiece(b, from_sq(m)));
    return captureEval + pieceEval;
}



// AlphaBetaMax with error/break code
/*
* int alphaBeta( int alpha, int beta, int depthleft ) {
if( depthleft == 0 ) return quiesce( alpha, beta );
for ( all moves)  {
score = -alphaBeta( -beta, -alpha, depthleft - 1 );
if( score >= beta )
return beta;   //  fail hard beta-cutoff
if( score > alpha )
alpha = score; // alpha acts like max in MiniMax
}
return alpha;
}*/

/*
// Self-called
// must be used at root depth ???
ReturnCode AlphaBetaNegmaxMax(int& score, Position& pos, ref Stack<Move> pv, int alpha, int beta, int depth, int maxDepth)
{
if (brainStatus != THINKING)
return STOPTHINKING; // quick exit
String pfen = pos.GetPseudoFEN();
if (depth == maxDepth)
{
score = EvaluateAbsolute(pos);
return OK;
}
vector<Move> moves = new vector<Move>();
GetLegalMoves(pos, ref moves);
if (moves.size() == 0)
if (IsCheck(pos))
{
score = ((currentTurn+maxDepth)%2 == 0 ? -1 : 1) * (VALUE_MATE - depth);
return OK;
}
else
{
score = VALUE_DRAW;
return OK;
}
nbProcessedNodes += moves.size();
foreach (Move m in moves)
{
pos.Play(m);
ReturnCode returnCode = AlphaBetaNegmaxMax(score, ref pos, ref pv, -beta, -alpha, depth + 1, maxDepth);
score *= -1;
pos.Unplay(m);
if (returnCode != OK)
return returnCode;
if (score >= beta)
{
score = beta;   // fail hard beta-cutoff
return OK;
}
if (score > alpha)
{
alpha = score; // alpha acts like max in MiniMax
}
}
score = alpha;
return OK;
}
*/

// Self-called
// TODO: problems for odd depth
//ReturnCode Engine::AlphaBetaNegmaxMin(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth)
//{
//	if (brainStatus != THINKING)
//		return STOPTHINKING; // quick exit
//	string pfen = pos.GetPseudoFEN();
//	if (depth == maxDepth)
//	{
//		//score = EvaluateAbsolute(pos);
//		//score = EvaluateRelative(pos,(currentTurn +depth + 1)%2); //almost ok
//		score = EvaluateRelative(pos, (Color)((currentTurn + (Color)(depth + 1)) % 2));
//		return OK;
//	}
//	vector<Move> moves;// = new vector<Move>();
//	GetLegalMoves(pos, moves);
//	if (moves.size() == 0)
//		if (IsCheck(pos))
//		{
//			score = (pos.GetTurn() == currentTurn) ? (depth - VALUE_MATE) : (VALUE_MATE - depth);
//			return OK;
//		}
//		else
//		{
//			score = VALUE_DRAW;
//			return OK;
//		}
//		nbProcessedNodes += moves.size();
//		//foreach (Move m in moves)
//		for(vector<Move>::iterator m = moves.begin(); m!=moves.end() ; m++)
//		//for(int i=0;i<moves.size();i++)
//		{
//			//Move m = moves.get(i);
//
//			pos.Play(*m);
//			ReturnCode returnCode = AlphaBetaNegmaxMin(score, pos, -beta, -alpha, depth + 1, maxDepth);
//			score *= -1;
//			pos.Unplay(*m);
//			if (returnCode != OK)
//				return returnCode;
//			if (score <= alpha)
//			{
//				score = alpha;   // fail hard alpha-cutoff
//				return OK;
//			}
//			if (score < beta)
//			{
//				beta = score; // beta acts like max in MiniMax
//			}
//		}
//		score = beta;
//		return OK;
//}

// AlphaBetaMax with error/break code
//ReturnCode Engine::AlphaBetaMax(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth)
//{
//	if (brainStatus != THINKING) 
//		return STOPTHINKING; // quick exit
//	//string pfen = pos.GetPseudoFEN();
//	if (depth == maxDepth)
//	{
//		//if (debugQuiesce)
//		//    return QuiesceMin(score, ref pos, alpha, beta, depth + 1, maxDepth + 1 + QUIESCE_DEPTH);
//		//else
//
//		score = EvaluateRelative(pos, this->GetTurn());
//		return OK;
//	}
//	//vector<Move> moves;// = new vector<Move>();
//	vector<Move> moves(256);//prepare move allocation to boost push back
//	GetLegalMoves(pos, moves);
//	if (moves.size() == 0)
//		if (IsCheck(pos))
//		{
//			score = -VALUE_MATE +depth;
//			return OK;
//		}
//		else
//		{
//			score = VALUE_DRAW;
//			return OK;
//		}
//		nbProcessedNodes += moves.size();
//		//foreach (Move m in moves)
//		for(vector<Move>::iterator m = moves.begin(); m!=moves.end() ; m++)
//			//for(int i=0;i<moves.size();i++)
//		{
//			//Move m = moves.get(i);
//
//			pos.Play(*m);
//			ReturnCode returnCode = AlphaBetaMin(score, pos, alpha, beta, depth + 1, maxDepth);
//			pos.Unplay(*m);
//			if (returnCode != OK){
//				//delete moves; // specific to moves pointer
//				return returnCode;
//			}
//			if (score >= beta)
//			{
//				score = beta;   // fail hard beta-cutoff
//				return OK;
//			}
//			if (score > alpha)
//			{
//				alpha = score; // alpha acts like max in MiniMax
//			}
//		}
//		score = alpha;
//		return OK;
//}

// AlphaBetaMin with error/break code
//ReturnCode Engine::AlphaBetaMin(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth)
//{
//	if (brainStatus != THINKING)
//		return STOPTHINKING; // quick exit
//	//string pfen = pos.GetPseudoFEN();
//	if (depth == maxDepth)
//	{
//		//if (debugQuiesce)
//		//    return QuiesceMax(score, ref pos, alpha, beta, depth + 1, maxDepth+1+QUIESCE_DEPTH);
//		//else
//		score = EvaluateRelative(pos, this->GetTurn());
//		return OK;
//	}
//	vector<Move> moves(256);//prepare move allocation to boost push back
//	GetLegalMoves(pos, moves);
//	if (moves.size() == 0)
//		if (IsCheck(pos))
//		{
//			score = VALUE_MATE - depth;
//			return OK;
//		}
//		else
//		{
//			score = VALUE_DRAW;
//			return OK;
//		}
//		nbProcessedNodes += moves.size();
//		//foreach (Move m in moves)
//		for(vector<Move>::iterator m = moves.begin(); m!=moves.end() ; m++)
//			//for(int i=0;i<moves.size();i++)
//		{
//			//Move m = moves.get(i);
//
//			pos.Play(*m);
//			ReturnCode returnCode = AlphaBetaMax(score, pos, alpha, beta, depth + 1, maxDepth);
//			pos.Unplay(*m);
//			if (returnCode != OK)
//				return returnCode;
//			if (score <= alpha)
//			{
//				score = alpha; // fail hard alpha-cutoff
//				return OK;
//			}
//			if (score < beta)
//			{
//				beta = score; // beta acts like min in MiniMax
//			}
//		}
//		score = beta;
//		return OK;
//}


// AlphaBetaMax with error/break code
ReturnCode Engine::AlphaBetaMax(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth, Move* pv)
{
	if (brainStatus != THINKING) 
		return STOPTHINKING; // quick exit
	if (depth == maxDepth)
	{
		score = EvaluateRelative(pos, this->GetTurn());
		pv[depth] = MOVE_NULL; // to stop the pv chain
		return OK;
	}
	vector<Move> moves(256);//prepare move allocation to boost push back
	GetLegalMoves(pos, moves);

	////debug
	//DisplayMoves(moves);

	if (moves.size() == 0)
		if (IsCheck(pos))
		{
			score = -VALUE_MATE +depth;
			pv[depth] = MOVE_NULL; // to stop the pv chain
			return OK;
		}
		else
		{
			score = VALUE_DRAW;
			pv[depth] = MOVE_NULL; // to stop the pv chain
			return OK;
		}
		nbProcessedNodes += moves.size();
		for(vector<Move>::iterator m = moves.begin(); m!=moves.end() ; m++)
		{
			pos.Play(*m);
			ReturnCode returnCode = AlphaBetaMin(score, pos, alpha, beta, depth + 1, maxDepth, pv);
			pos.Unplay(*m);
			if (returnCode != OK)
			{
				return returnCode;
			}
			if (score >= beta)
			{
				score = beta;   // fail hard beta-cutoff
				return OK;
			}
			if (score > alpha)
			{
				alpha = score; // alpha acts like max in MiniMax
				// keeps potential pv move
				pv[depth] = *m;
			}
		}
		score = alpha;
		return OK;
}

// AlphaBetaMin with error/break code
ReturnCode Engine::AlphaBetaMin(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth, Move* pv)
{
	if (brainStatus != THINKING)
		return STOPTHINKING; // quick exit
	if (depth == maxDepth)
	{
		score = EvaluateRelative(pos, this->GetTurn());
		pv[depth] = MOVE_NULL; // to stop the pv chain
		return OK;
	}
	vector<Move> moves(256);//prepare move allocation to boost push back
	GetLegalMoves(pos, moves);
	
	////debug
	//DisplayMoves(moves);
	
	if (moves.size() == 0)
		if (IsCheck(pos))
		{
			score = VALUE_MATE - depth;
			pv[depth] = MOVE_NULL; // to stop the pv chain
			return OK;
		}
		else
		{
			score = VALUE_DRAW;
			pv[depth] = MOVE_NULL; // to stop the pv chain
			return OK;
		}
		nbProcessedNodes += moves.size();
		for(vector<Move>::iterator m = moves.begin(); m!=moves.end() ; m++)
		{
			pos.Play(*m);
			ReturnCode returnCode = AlphaBetaMax(score, pos, alpha, beta, depth + 1, maxDepth, pv);
			pos.Unplay(*m);
			if (returnCode != OK)
				return returnCode;
			if (score <= alpha)
			{
				score = alpha; // fail hard alpha-cutoff
				return OK;
			}
			if (score < beta)
			{
				beta = score; // beta acts like min in MiniMax
				// keeps potential pv move
				pv[depth] = *m;
			}
		}
		score = beta;
		return OK;
}

/*
ReturnCode QuiesceMax(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth)
{
if (depth > selectiveDepth)
selectiveDepth = depth;

int stand_pat = EvaluateRelative(pos, currentTurn);

if (depth >= maxDepth)
{
score = stand_pat;
return OK;
}


if (stand_pat >= beta)
{
score = beta;
return OK;
}
if (alpha < stand_pat)
alpha = stand_pat;

vector<Move> captures = new vector<Move>();
GetLegalMoves(pos, ref captures);

foreach (Move m in captures)
{

pos.Play(m);
//score = -Quiesce( -beta, -alpha );
QuiesceMax(score, ref pos, -beta, -alpha, depth+1, maxDepth);
pos.Unplay(m);

if (score >= beta)
{
score = beta;
return OK;
}
if (score > alpha)
alpha = score;
}
score = alpha;
return OK;
}


ReturnCode QuiesceMin(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth)
{
if (depth > selectiveDepth)
selectiveDepth = depth;

int stand_pat = EvaluateRelative(pos, currentTurn);

if (depth >= maxDepth)
{
score = stand_pat;
return OK;
}

if (stand_pat <= alpha)
{
score = alpha;
return OK;
}
if (beta > stand_pat)
beta = stand_pat;

vector<Move> captures = new vector<Move>();
GetLegalMoves(pos, ref captures);

foreach (Move m in captures)
{
// captures comes first in move list
// when the first quiet move comes there is no more capture after that
if (!m.IsCapture())
break;

pos.Play(m);
//score = -Quiesce( -beta, -alpha );
QuiesceMax(score, ref pos, -beta, -alpha, depth+1, maxDepth);
pos.Unplay(m);

if (score <= alpha)
{
score = alpha;
return OK;
}
if (score < beta)
beta = score;
}
score = beta;
return OK;
}
* */

///*function negascout(node, depth, α, β)
//if node is a terminal node or depth = 0
//return the heuristic value of node
//b := β                                             (* initial window is (-β, -α) *)
//foreach child of node
//score := -negascout (child, depth - 1, -b, -α)
//if α < score < β and child is not first child      (* check if null-window failed high *)
//score := -negascout(child, depth - 1, -β, -α)  (* full re-search *)
//α := max(α, score)
//if α ≥ β
//return α                                   (* Beta cut-off *)
//b := α + 1                                     (* set new null window *)
//return α*/
//ReturnCode Engine::NegaScout(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth)
//{
//	if (brainStatus != THINKING)
//		return STOPTHINKING; // quick exit
//	string pfen = pos.GetPseudoFEN();
//	if (depth == maxDepth)
//	{
//		score = EvaluateAbsolute(pos);
//		return OK;
//	}
//	int b = beta;
//	vector<Move> moves;// = new vector<Move>();
//	nbProcessedNodes += GetLegalMoves(pos, moves);
//
//	bool isFirstMove = true;
//	//foreach (Move m in moves)
//	for(vector<Move>::iterator m = moves.begin(); m!=moves.end() ; m++)
//	//for(int i=0;i<moves.size();i++)
//	{
//		//Move m = moves.get(i);
//
//		pos.Play(*m);
//		ReturnCode returnCode = NegaScout(score, pos, -b , -alpha, depth + 1, maxDepth);
//		pos.Unplay(*m);
//		if (returnCode != OK)
//			return returnCode;
//
//		if ((alpha < score) && (score < beta) && (!isFirstMove))
//		{
//			pos.Play(*m);
//			returnCode = NegaScout(score, pos, -b, -alpha, depth + 1, maxDepth);
//			pos.Unplay(*m);
//			if (returnCode != OK)
//				return returnCode;
//			alpha = max(alpha, score);
//			if (alpha >= beta)
//			{
//				score = alpha;
//				return OK;
//			}
//			b = alpha + 1;
//		}
//
//		isFirstMove = false;
//	}
//	score = alpha;
//	return OK;
//}


// A try for the iterative version
//ReturnCode Engine::AlphaBetaIterative(int& score, Position& position, int alpha, int beta, int depth, int maxDepth)
//{
//	/*
//	// create a copy free to be left draft if the calculation is not complete
//	Position& pos = new Position(position);
//
//	vector<Move> moves = new vector<Move>();
//	const int STACKOFFSET = 2;
//	int L = maxDepth;
//	// alpha,beta will not be used (only for compatibility)
//	int l; // level of recursion
//	int[] a = new int[L + STACKOFFSET]; // stack for recusrsion where a[l-2],a[l-1],a[l],a[l+1] denote respectively alpha, -beta, m, -t in procedure F2
//	IterativePosStackItem[] r = new IterativePosStackItem[L + 1]; // another stack of recursion where r[l] and r[l+1] denotes respectively p and q in F2
//	l = 0;
//	a[0] = a[1] = -VALUE_INFINITE;
//	r[0] = new IterativePosStackItem(pos);
//
//	F2:
//	// quick exit
//	if (brainStatus != THINKING)
//	return STOPTHINKING;
//
//	//moves.Clear(); //GetLegalMoves(r[l].position, moves);
//	//r[l + 1] = first(r[l]);
//	if (IsCheck(pos))
//	{
//
//	goto done;
//	}
//
//	r[l + 1] = new IterativePosStackItem(pos);
//
//	//if(r[l+1]==V) 
//	if (r[l + 1].End() && Engine.IsCheck(pos)) 
//	{
//	//pos.Unplay(r[l + 1].GetMove()); // undo before l--
//	a[l + STACKOFFSET] = VALUE_MATE - 1 - l; // + or - with -l offset
//	goto done;
//	}
//	if (((l + 1) >= L) || r[l + 1].End()) 
//	{
//	//pos.Unplay(r[l].GetMove()); // undo before l--
//	// eval of r(l)
//	a[l + STACKOFFSET] = Engine.EvaluateAbsolute(pos);
//	goto done;
//	}
//	// begin
//	pos.Play(r[l + 1].GetMove());
//
//	a[l + STACKOFFSET] = a[l];
//	loop:
//	l++; goto F2;
//	resume:
//	if (-a[l + 1 + STACKOFFSET] > a[l + STACKOFFSET])
//	// begin
//	{
//	a[l + STACKOFFSET] = -a[l + 1 + STACKOFFSET];
//	if (a[l + 1 + STACKOFFSET] <= a[l - 1 + STACKOFFSET])
//	{
//	//pos.Unplay(r[l].GetMove()); // undo before l--
//	goto done;
//	}
//	}// end
//	//r[l + 1] = next(r[l + 1]);
//	//pos.Unplay(r[l + 1].GetMove());
//	r[l + 1].Next();
//	//if (r[l + 1] != V)
//	if (!r[l + 1].End())
//	{
//	//pos.Unplay(r[l+1].GetMove());
//	goto loop;
//	}
//
//	done:
//	//pos.Unplay(r[l + 1].GetMove());
//	pos.Unplay(r[l].GetMove());
//
//	l--;
//	if (l >= 0)
//	goto resume;
//	score = a[0 + STACKOFFSET];
//	*/
//	return OK;
//}


/*
* Stop the main injector loop
* 
*/ 
void Engine::Stop()
{
	brainStatus = PAUSED;
	watch.Reset();
	isSearchMove = false;
}

/*
* Start the main injector loop
* 
*/ 
void Engine::Start()
{
	watch.Start();
	// Must be done before each Search (this the corresp. function)
	//TT.Clear();
	nbProcessedNodes = 0;
	if (!isSearchMove)
	{
		moves.clear();
		// New alphabeta implementation
		GetLegalMoves(currentPosition, moves);
	} // else moves are already provided

	if(debugMode)
		DisplayMoves(moves);

	//watch.Start();
	// reajust thinking time
	if (brainMode == ADJUSTTIME_MODE)
		UpdateThinkDelay();

	//cout << "Switch to THINKING status" << endl;
	brainStatus = THINKING;
}

void Engine::DisplayMoves(vector<Move>& moves)
{
	//StringBuilder sb = new StringBuilder();
	ostringstream sb;
	sb << "info #### MOVES TO EVALUATE (";
	sb << moves.size();
	sb << ")";

	//foreach (Move m in moves)
	for(vector<Move>::iterator m = moves.begin(); m!=moves.end() ; m++)
		//for(int i=0;i<moves.size();i++)
	{
		//Move m = moves.get(i);
		sb << move_to_string(*m) << ' ';
	}
	cout << sb.str() << endl;
}

void Engine::SetDebug(bool isDebugOn)
{
	debugMode = isDebugOn;
}

void Engine::SetOption(string name, string value)
{
	//TODO: implement options with a hashtable and a struct 'option'
	/*
	switch(name)
	{

	// Simplification => param names without spaces
	case "PawnValue":
	this->SetPawnValue(Int32.Parse(value));
	break;
	case "KnightValue":
	this->SetKnightValue(Int32.Parse(value));
	break;
	case "BishopValue":
	this->SetBishopValue(Int32.Parse(value));
	break;
	case "RookValue":
	this->SetRookValue(Int32.Parse(value));
	break;
	case "QueenValue":
	this->SetRookValue(Int32.Parse(value));
	break;

	case "Smart Timing":
	this->SetSmartTiming(value == "on");
	break;
	default:
	cout << "Unknown option: "  << name << endl;
	break;
	}* */
	cout << "Unknown option: "  << name << endl;
}

/*
float Engine::AttackFactor(Position& position)
{
vector<Move> moves;// = new vector<Move>();
GetLegalMoves(position, moves);
if (moves.size() == 0)
return 0;
int attackCount = 0;
for(vector<Move>::iterator m = moves.begin(); m!=moves.end() ; m++)
//foreach (Move m in moves)
{
if ((*m).IsCapture())
attackCount++;
}

return attackCount / moves.size();
}
*/


// Give the list of possible moves
void Engine::GetPossibleMoves(Position& position, vector<Move> & moves)
{
	moves.clear();
	for (Square square = SQ_A1; square <= SQ_H8; square++)
	{
		if (!position.IsEmptyOrOpponent(square))
		{
			GetPossibleMoves(position, square, moves);
		}
	}
}

// Give the list of possible captures
void Engine::GetPossibleCaptures(Position& position, vector<Move> & moves)
{
	moves.clear();
	for (Square square = SQ_A1; square <= SQ_H8; square++)
	{
		if (!position.IsEmptyOrOpponent(square))
		{
			GetPossibleCaptures(position, square, moves);
		}
	}
}

bool Engine::IsCheck(Position& position)
{
	// new bitboard implementation
	return position.IsCheck(position.GetTurn());
	// old implementation
	//return IsCheck(Position&, position.GetTurn());
}


// TESTING ONLY !!! (bad perfs)
bool Engine::IsCheckmate(Position& position)
{
	// new bitboard implementation
	vector<Move> lm;
	GetLegalMoves(position, lm);

	return IsCheck(position) && lm.empty();
	// old implementation
	//return IsCheck(Position&, position.GetTurn());
}

/**
* Indicate if the currentPosition is check
* for the given player
* @param currentPosition
* @return
*/
/*
bool Engine::IsCheck(Position& position, Color playerColor)
{
//TODO: modify return block to take chessmen power/given check frequency into account
Piece king = playerColor ? W_KING : B_KING;
Piece oKing = playerColor ? B_KING : W_KING;
Piece oQueen = playerColor ? B_QUEEN : W_QUEEN;
Piece oRook = playerColor ? B_ROOK : W_ROOK;
Piece oBish = playerColor ? B_BISHOP : W_BISHOP;
Piece oKnig = playerColor ? B_KNIGHT : W_KNIGHT;
Piece oPawn = playerColor ? B_PAWN : W_PAWN;
Color oColor = (Color)1-playerColor;

// get king square
Square square;
for (square = SQ_A1; square <= SQ_H8; square++)
if (position.GetPiece(square) == king)
break;
assert((square >= SQ_A1) && (square <= SQ_H8));

int col = file_of((Square)square);
int row = rank_of((Square)square);


// king threat (virtual)
if (col > FILE_A)
if (position.GetPiece(square + DELTA_W) == oKing)
return true;
if (col > FILE_A && row < RANK_8)
if (position.GetPiece(square + DELTA_NW) == oKing)
return true;
if (row < RANK_8)
if (position.GetPiece(square + DELTA_N) == oKing)
return true;
if (col < FILE_H && row < RANK_8)
if (position.GetPiece(square + DELTA_NE) == oKing)
return true;
if (col < FILE_H)
if (position.GetPiece(square + DELTA_E) == oKing)
return true;
if (col < FILE_H && row > RANK_1)
if (position.GetPiece(square + DELTA_SE) == oKing)
return true;
if (row > RANK_1)
if (position.GetPiece(square + DELTA_S) == oKing)
return true;
if (col > FILE_A && row > RANK_1)
if (position.GetPiece(square + DELTA_SW) == oKing)
return true;

// Pawn threat
if (playerColor == WHITE)
{
if (col > FILE_A && row < RANK_8)
if (position.GetPiece(square + DELTA_NW) == oPawn)
return true;
if (col < FILE_H && row < RANK_8)
if (position.GetPiece(square + DELTA_NE) == oPawn)
return true;
}
else
{
if (col > FILE_A && row > RANK_1)
if (position.GetPiece(square + DELTA_SW) == oPawn)
return true;
if (col < FILE_H && row > RANK_1)
if (position.GetPiece(square + DELTA_SE) == oPawn)
return true;
}

// Knight threat
if (col > FILE_A && row < RANK_7 && position.IsEmptyOrColor(square + DELTA_N + DELTA_NW, oColor))
if (position.GetPiece(square + DELTA_N + DELTA_NW) == oKnig)
return true;
if (col < FILE_H && row < RANK_7 && position.IsEmptyOrColor(square + DELTA_N + DELTA_NE, oColor))
if (position.GetPiece(square + DELTA_N + DELTA_NE) == oKnig)
return true;
if (col < FILE_G && row < RANK_8 && position.IsEmptyOrColor(square + DELTA_E + DELTA_NE, oColor))
if (position.GetPiece(square + DELTA_E + DELTA_NE) == oKnig)
return true;
if (col < FILE_G && row > RANK_1 && position.IsEmptyOrColor(square + DELTA_E + DELTA_SE, oColor))
if (position.GetPiece(square + DELTA_E + DELTA_SE) == oKnig)
return true;
if (col < FILE_H && row > RANK_2 && position.IsEmptyOrColor(square + DELTA_S + DELTA_SE, oColor))
if (position.GetPiece(square + DELTA_S + DELTA_SE) == oKnig)
return true;
if (col > FILE_A && row > RANK_2 && position.IsEmptyOrColor(square + DELTA_S + DELTA_SW, oColor))
if (position.GetPiece(square + DELTA_S + DELTA_SW) == oKnig)
return true;
if (col > FILE_B && row > RANK_1 && position.IsEmptyOrColor(square + DELTA_W + DELTA_SW, oColor))
if (position.GetPiece(square + DELTA_W + DELTA_SW) == oKnig)
return true;
if (col > FILE_B && row < RANK_8 && position.IsEmptyOrColor(square + DELTA_W + DELTA_NW, oColor))
if (position.GetPiece(square + DELTA_W + DELTA_NW) == oKnig)
return true;


// bishop&queen threat
// Up-right moves
Square sqStart = square + DELTA_NE;
Square sqEnd = (Square)min(64, square + 9 * (8 - col));
for (Square sq = sqStart; sq < sqEnd; sq += DELTA_NE)
{
if (position.IsEmptyOrColor(sq, oColor))
{
Piece p = position.GetPiece(sq);
if ((p == oBish) || (p == oQueen))
return true;
if (p != NO_PIECE)
break;
}
else break;
}

// Down-right moves
sqStart = square + DELTA_SE;
sqEnd = (Square)max(-1, square - 7 * (8 - col));
for (Square sq = sqStart; sq > sqEnd; sq += DELTA_SE)
{
if (position.IsEmptyOrColor(sq, oColor))
{
Piece p = position.GetPiece(sq);
if ((p == oBish) || (p == oQueen))
return true;
if (p != NO_PIECE)
break;
}
else break;
}

// Down-left moves
sqStart = square + DELTA_SW;
sqEnd = (Square)max(-1, square - 9 * (col + 1));
for (Square sq = sqStart; sq > sqEnd; sq += DELTA_SW)
{
if (position.IsEmptyOrColor(sq, oColor))
{
Piece p = position.GetPiece(sq);
if ((p == oBish) || (p == oQueen))
return true;
if (p != NO_PIECE)
break;
}
else break;
}
// Up-left moves
sqStart = square + DELTA_NW;
sqEnd = (Square)min(64, square + 7 * (col + 1));
for (Square sq = sqStart; sq < sqEnd; sq += DELTA_NW)
{
if (position.IsEmptyOrColor(sq, oColor))
{
Piece p = position.GetPiece(sq);
if ((p == oBish) || (p == oQueen))
return true;
if (p != NO_PIECE)
break;
}
else break;
}


// rook&queen threat
// Horizontal moves right
sqStart = square + DELTA_E;
sqEnd = (Square)(8 * (row + 1));
for (Square sq = sqStart; sq < sqEnd; sq++)
{
if (position.IsEmptyOrColor(sq, oColor))
{
Piece p = position.GetPiece(sq);
if ((p == oRook) || (p == oQueen))
return true;
if (p != NO_PIECE)
break;
}
else break;
}

// Horizontal moves left
sqStart = square + DELTA_W;
sqEnd = (Square)(8 * row - 1);
for (Square sq = sqStart; sq > sqEnd; sq--)
{
if (position.IsEmptyOrColor(sq, oColor))
{
Piece p = position.GetPiece(sq);
if ((p == oRook) || (p == oQueen))
return true;
if (p != NO_PIECE)
break;
}
else break;
}

// Vertical moves up
sqStart = square + DELTA_N;
sqEnd = (Square)64;
for (Square sq = sqStart; sq < sqEnd; sq += DELTA_N)
{
if (position.IsEmptyOrColor(sq, oColor))
{
Piece p = position.GetPiece(sq);
if ((p == oRook) || (p == oQueen))
return true;
if (p != NO_PIECE)
break;
}
else break;
}
// Vertical moves down
sqStart = square + DELTA_S;
sqEnd = (Square)-1;
for (Square sq = sqStart; sq > sqEnd; sq+=DELTA_S)
{
if (position.IsEmptyOrColor(sq, oColor))
{
Piece p = position.GetPiece(sq);
if ((p == oRook) || (p == oQueen))
return true;
if (p != NO_PIECE)
break;
}
else break;
}

return false;

}
*/

/**
* Get the possible moves for one piece
* 
* @param currentPosition
* @param square
* @return
*/
void Engine::GetPossibleMoves(Position& position, Square square, vector<Move> & possibleMoves)
{
	Piece pieceCode = position.GetPiece(square);

	if ((position.IsBlackToMove() && position.IsPieceBlack(pieceCode))
		|| (position.IsWhiteToMove() && position.IsPieceWhite(pieceCode)))
	{

		switch (pieceCode)
		{
		case W_PAWN:
		case B_PAWN:
			GetPossiblePawnMoves(position, square, possibleMoves);
			break;
		case W_BISHOP:
		case B_BISHOP:
			GetPossibleBishopMoves(position, square, possibleMoves);
			break;
		case W_KNIGHT:
		case B_KNIGHT:
			GetPossibleKnightMoves(position, square, possibleMoves);
			break;
		case W_ROOK:
		case B_ROOK:
			GetPossibleRookMoves(position, square, possibleMoves);
			break;
		case W_QUEEN:
		case B_QUEEN:
			GetPossibleQueenMoves(position, square, possibleMoves);
			break;
		case W_KING:
		case B_KING:
			GetPossibleKingMoves(position, square, possibleMoves);
			break;
		default:
			break;

		}
	}
}

void Engine::GetPossibleCaptures(Position& position, Square square, vector<Move> & possibleMoves)
{
	Piece pieceCode = position.GetPiece(square);

	if ((position.IsBlackToMove() && position.IsPieceBlack(pieceCode))
		|| (position.IsWhiteToMove() && position.IsPieceWhite(pieceCode)))
	{

		switch (pieceCode)
		{
		case W_PAWN:
		case B_PAWN:
			GetPossiblePawnCaptures(position, square, possibleMoves);
			break;
		case W_BISHOP:
		case B_BISHOP:
			GetPossibleBishopCaptures(position, square, possibleMoves);
			break;
		case W_KNIGHT:
		case B_KNIGHT:
			GetPossibleKnightCaptures(position, square, possibleMoves);
			break;
		case W_ROOK:
		case B_ROOK:
			GetPossibleRookCaptures(position, square, possibleMoves);
			break;
		case W_QUEEN:
		case B_QUEEN:
			GetPossibleQueenCaptures(position, square, possibleMoves);
			break;
		case W_KING:
		case B_KING:
			GetPossibleKingCaptures(position, square, possibleMoves);
			break;
		default:
			break;

		}
	}
}

/**
* Get possible moves for King
* 
* @param currentPosition
* @param square
* @return
*/
void Engine::GetPossibleKingMoves(Position& position, Square square, vector<Move> & possibleKingMoves)
{
	File col = file_of(square);
	Rank row = rank_of(square);

	if (col < FILE_H && position.IsEmptyOrOpponent(square+DELTA_E))
	{
		//Move move = make_move(square, square+DELTA_E);
		possibleKingMoves.push_back(make_move(square, square+DELTA_E));
	}
	if (col < FILE_H && row < RANK_8 && position.IsEmptyOrOpponent(square+DELTA_NE))
	{
		//Move move = make_move(square, square+DELTA_NE);
		possibleKingMoves.push_back(make_move(square, square+DELTA_NE));
	}
	if (row < RANK_8 && position.IsEmptyOrOpponent(square+DELTA_N))
	{
		//Move move = make_move(square, square+DELTA_N);
		possibleKingMoves.push_back(make_move(square, square+DELTA_N));
	}
	if (col > FILE_A && row < RANK_8 && position.IsEmptyOrOpponent(square+DELTA_NW))
	{
		//Move move = make_move(square, square+DELTA_NW);
		possibleKingMoves.push_back(make_move(square, square+DELTA_NW));
	}
	if (col > FILE_A && position.IsEmptyOrOpponent(square+DELTA_W))
	{
		//Move move = make_move(square, square+DELTA_W);
		possibleKingMoves.push_back(make_move(square, square+DELTA_W));
	}
	if (col > FILE_A && row > RANK_1 && position.IsEmptyOrOpponent(square+DELTA_SW))
	{
		//Move move = make_move(square, square+DELTA_SW);
		possibleKingMoves.push_back(make_move(square, square+DELTA_SW));
	}
	if (row > RANK_1 && position.IsEmptyOrOpponent(square+DELTA_S))
	{
		//Move move = make_move(square, square+DELTA_S);
		possibleKingMoves.push_back(make_move(square, square+DELTA_S));
	}
	if (col < FILE_H && row > RANK_1 && position.IsEmptyOrOpponent(square+DELTA_SE))
	{
		//Move move = make_move(square, square+DELTA_SE);
		possibleKingMoves.push_back(make_move(square, square+DELTA_SE));
	}

	if (position.GetTurn()==WHITE)
	{
		if (position.gameState.whiteCanCastleQueenSide)
			if(position.IsEmpty(SQ_B1))
				if( position.IsEmpty(SQ_C1))
					if(position.IsEmpty(SQ_D1))
					{
						//Move subMove = make_move(SQ_E1, SQ_D1);
						//position.Play(subMove);
						bool isCheck = position.IsCheck(WHITE, SQ_D1);
						//position.Unplay(subMove);
						if (!isCheck)
							possibleKingMoves.push_back(make_move(SQ_E1, SQ_C1));
					}
		if (position.gameState.whiteCanCastleKingSide)
			if(position.IsEmpty(SQ_E1))
				if(position.IsEmpty(SQ_F1))
				{
					//Move subMove = make_move(SQ_E1, SQ_F1);
					//position.Play(subMove);
					bool isCheck = position.IsCheck(WHITE, SQ_F1);
					//position.Unplay(subMove);
					if (isCheck)
						possibleKingMoves.push_back(make_move(SQ_E1, SQ_G1));
				}
	}
	else
	{
		if (position.gameState.blackCanCastleQueenSide)
			if(position.IsEmpty(SQ_B8))
				if(position.IsEmpty(SQ_C8))
					if(position.IsEmpty(SQ_D8))
					{
						//Move subMove = make_move(SQ_E8, SQ_D8);
						//position.Play(subMove);
						bool isCheck = position.IsCheck(BLACK, SQ_D8);
						//position.Unplay(subMove);
						if (!isCheck)
							possibleKingMoves.push_back(make_move(SQ_E8, SQ_C8));
					}
		if (position.gameState.blackCanCastleKingSide)
			if(position.IsEmpty(SQ_E8))
				if(position.IsEmpty(SQ_F8))
				{
					//Move subMove = make_move(SQ_E8, SQ_F8);
					//position.Play(subMove);
					bool isCheck = position.IsCheck(WHITE, SQ_F8);;
					//position.Unplay(subMove);
					if (!isCheck)
						possibleKingMoves.push_back(make_move(SQ_E8, SQ_G8));
				}
	}
}

void Engine::GetPossibleKingCaptures(Position& position, Square square, vector<Move> & possibleKingMoves)
{
	File col = file_of(square);
	Rank row = rank_of(square);

	if (col < FILE_H && position.IsOpponent(square+DELTA_E))
	{
		//Move move = make_move(square, square+DELTA_E);
		possibleKingMoves.push_back(make_move(square, square+DELTA_E));
	}
	if (col < FILE_H && row < RANK_8 && position.IsOpponent(square+DELTA_NE))
	{
		//Move move = make_move(square, square+DELTA_NE);
		possibleKingMoves.push_back(make_move(square, square+DELTA_NE));
	}
	if (row < RANK_8 && position.IsOpponent(square+DELTA_N))
	{
		//Move move = make_move(square, square+DELTA_N);
		possibleKingMoves.push_back(make_move(square, square+DELTA_N));
	}
	if (col > FILE_A && row < RANK_8 && position.IsOpponent(square+DELTA_NW))
	{
		//Move move = make_move(square, square+DELTA_NW);
		possibleKingMoves.push_back(make_move(square, square+DELTA_NW));
	}
	if (col > FILE_A && position.IsOpponent(square+DELTA_W))
	{
		//Move move = make_move(square, square+DELTA_W);
		possibleKingMoves.push_back(make_move(square, square+DELTA_W));
	}
	if (col > FILE_A && row > RANK_1 && position.IsOpponent(square+DELTA_SW))
	{
		//Move move = make_move(square, square+DELTA_SW);
		possibleKingMoves.push_back(make_move(square, square+DELTA_SW));
	}
	if (row > RANK_1 && position.IsOpponent(square+DELTA_S))
	{
		//Move move = make_move(square, square+DELTA_S);
		possibleKingMoves.push_back(make_move(square, square+DELTA_S));
	}
	if (col < FILE_H && row > RANK_1 && position.IsOpponent(square+DELTA_SE))
	{
		//Move move = make_move(square, square+DELTA_SE);
		possibleKingMoves.push_back(make_move(square, square+DELTA_SE));
	}
}

/**
* Get possible moves for Queen
* 
* @param currentPosition
* @param square
* @return
*/
void Engine::GetPossibleQueenMoves(Position& position, Square square, vector<Move> & possibleQueenMoves)
{
	GetPossibleRookMoves(position, square, possibleQueenMoves);
	GetPossibleBishopMoves(position, square, possibleQueenMoves);
}

void Engine::GetPossibleQueenCaptures(Position& position, Square square, vector<Move> & possibleQueenCaptures)
{
	GetPossibleRookCaptures(position, square, possibleQueenCaptures);
	GetPossibleBishopCaptures(position, square, possibleQueenCaptures);
}

/*
* Get possible moves for Rook
*/
void Engine::GetPossibleRookMoves(Position& position, Square square, vector<Move> & possibleRookMoves)
{
	File col = file_of(square);
	Rank row = rank_of(square);

	// Horizontal moves right
	Square sqStart = square + DELTA_E;
	Square sqEnd = (Square)(8 * (row+1));
	for (Square sq = sqStart; sq < sqEnd; sq++)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleRookMoves.push_back(make_move(square, sq));
			if (!position.IsEmpty(sq))
				break;
		}
		else
			break;
	}

	// Horizontal moves left
	sqStart = square + DELTA_W;
	sqEnd = (Square)(8 * row - 1);
	for (Square sq = sqStart; sq > sqEnd; sq--)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleRookMoves.push_back(make_move(square, sq));
			if (!position.IsEmpty(sq))
				break;
		}
		else
			break;
	}


	// Vertical moves up
	sqStart = (Square)(square + 8);
	sqEnd = (Square)64;
	for (Square sq = sqStart; sq < sqEnd; sq+=DELTA_N)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleRookMoves.push_back(make_move(square, sq));
			if (!position.IsEmpty(sq))
				break;
		}
		else
			break;
	}

	// Vertical moves down
	sqStart = square + DELTA_S;
	sqEnd = (Square)-1;
	for (Square sq = sqStart; sq > sqEnd; sq+=DELTA_S)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleRookMoves.push_back(make_move(square, sq));
			if (!position.IsEmpty(sq))
				break;
		}
		else
			break;
	}
}

void Engine::GetPossibleRookCaptures(Position& position, Square square, vector<Move> & possibleRookMoves)
{
	File col = file_of(square);
	Rank row = rank_of(square);

	// Horizontal moves right
	Square sqStart = square + DELTA_E;
	Square sqEnd = (Square)(8 * (row+1));
	for (Square sq = sqStart; sq < sqEnd; sq++)
	{
		if (position.IsOpponent(sq))
		{
			possibleRookMoves.push_back(make_move(square, sq));
		}
		else if (!position.IsEmpty(sq))
				break;
	}

	// Horizontal moves left
	sqStart = square + DELTA_W;
	sqEnd = (Square)(8 * row - 1);
	for (Square sq = sqStart; sq > sqEnd; sq--)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleRookMoves.push_back(make_move(square, sq));
		}
		else
			if (!position.IsEmpty(sq))
				break;
	}


	// Vertical moves up
	sqStart = (Square)(square + 8);
	sqEnd = (Square)64;
	for (Square sq = sqStart; sq < sqEnd; sq+=DELTA_N)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleRookMoves.push_back(make_move(square, sq));
		}
		else
			if (!position.IsEmpty(sq))
				break;
		break;
	}

	// Vertical moves down
	sqStart = square + DELTA_S;
	sqEnd = (Square)-1;
	for (Square sq = sqStart; sq > sqEnd; sq+=DELTA_S)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleRookMoves.push_back(make_move(square, sq));
		}
		else
			if (!position.IsEmpty(sq))
				break;
	}
}

/**
* Get possible moves for Knight
* 
* @param currentPosition
* @param square
* @return
*/
void Engine::GetPossibleKnightMoves(Position& position, Square square, vector<Move> & possibleKnightMoves)
{
	//vector<Move> possibleKnightMoves = new vector<Move>();
	int col = file_of(square);
	int row = rank_of(square);

	if (col > FILE_A && row < RANK_7 && position.IsEmptyOrOpponent(square + DELTA_N + DELTA_NW))
	{
		//Move move = make_move(square, square+15);
		//Move move = make_move(square, square + DELTA_N + DELTA_NW);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_N + DELTA_NW));
	}
	if (col < FILE_H && row < RANK_7 && position.IsEmptyOrOpponent(square+ DELTA_N + DELTA_NE))
	{
		//Move move = make_move(square, square + DELTA_N + DELTA_NE);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_N + DELTA_NE));
	}
	if (col < FILE_G && row < RANK_8 && position.IsEmptyOrOpponent(square+ DELTA_E + DELTA_NE))
	{
		//Move move = make_move(square, square + DELTA_E + DELTA_NE);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_E + DELTA_NE));
	}
	if (col < FILE_G && row > RANK_1 && position.IsEmptyOrOpponent(square+ DELTA_E + DELTA_SE))
	{
		//Move move = make_move(square, square + DELTA_E + DELTA_SE);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_E + DELTA_SE));
	}
	if (col < FILE_H && row > RANK_2 && position.IsEmptyOrOpponent(square+ DELTA_S + DELTA_SE))
	{
		//Move move = make_move(square, square + DELTA_S + DELTA_SE);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_S + DELTA_SE));
	}
	if (col > FILE_A && row > RANK_2 && position.IsEmptyOrOpponent(square+ DELTA_S + DELTA_SW))
	{
		//Move move = make_move(square, square + DELTA_S + DELTA_SW);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_S + DELTA_SW));
	}
	if (col > FILE_B && row > RANK_1  && position.IsEmptyOrOpponent(square+ DELTA_W + DELTA_SW))
	{
		//Move move = make_move(square, square + DELTA_W + DELTA_SW);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_W + DELTA_SW));
	}
	if (col > FILE_B && row < RANK_8 && position.IsEmptyOrOpponent(square + DELTA_W + DELTA_NW))
	{
		//Move move = make_move(square, square + DELTA_W + DELTA_NW);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_W + DELTA_NW));
	}
}

void Engine::GetPossibleKnightCaptures(Position& position, Square square, vector<Move> & possibleKnightMoves)
{
	//vector<Move> possibleKnightMoves = new vector<Move>();
	int col = file_of(square);
	int row = rank_of(square);

	if (col > FILE_A && row < RANK_7 && position.IsOpponent(square + DELTA_N + DELTA_NW))
	{
		//Move move = make_move(square, square+15);
		//Move move = make_move(square, square + DELTA_N + DELTA_NW);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_N + DELTA_NW));
	}
	if (col < FILE_H && row < RANK_7 && position.IsOpponent(square+ DELTA_N + DELTA_NE))
	{
		//Move move = make_move(square, square + DELTA_N + DELTA_NE);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_N + DELTA_NE));
	}
	if (col < FILE_G && row < RANK_8 && position.IsOpponent(square+ DELTA_E + DELTA_NE))
	{
		//Move move = make_move(square, square + DELTA_E + DELTA_NE);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_E + DELTA_NE));
	}
	if (col < FILE_G && row > RANK_1 && position.IsOpponent(square+ DELTA_E + DELTA_SE))
	{
		//Move move = make_move(square, square + DELTA_E + DELTA_SE);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_E + DELTA_SE));
	}
	if (col < FILE_H && row > RANK_2 && position.IsOpponent(square+ DELTA_S + DELTA_SE))
	{
		//Move move = make_move(square, square + DELTA_S + DELTA_SE);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_S + DELTA_SE));
	}
	if (col > FILE_A && row > RANK_2 && position.IsOpponent(square+ DELTA_S + DELTA_SW))
	{
		//Move move = make_move(square, square + DELTA_S + DELTA_SW);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_S + DELTA_SW));
	}
	if (col > FILE_B && row > RANK_1  && position.IsOpponent(square+ DELTA_W + DELTA_SW))
	{
		//Move move = make_move(square, square + DELTA_W + DELTA_SW);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_W + DELTA_SW));
	}
	if (col > FILE_B && row < RANK_8 && position.IsOpponent(square + DELTA_W + DELTA_NW))
	{
		//Move move = make_move(square, square + DELTA_W + DELTA_NW);
		possibleKnightMoves.push_back(make_move(square, square + DELTA_W + DELTA_NW));
	}
}

/**
* Get possible moves for Bishop
* 
* @param currentPosition
* @param square
* @return
*/
void Engine::GetPossibleBishopMoves(Position& position, Square square, vector<Move> & possibleBishopMoves)
{
	//vector<Move> possibleBishopMoves = new vector<Move>();
	File col = file_of(square);
	Rank row = rank_of(square);

	// Up-right moves
	Square sqStart = square + DELTA_NE;
	Square sqEnd = (Square)min(64, square + 9 * (8 - col));
	for (Square sq = sqStart; sq < sqEnd; sq += DELTA_NE)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleBishopMoves.push_back(make_move(square, sq));
			if (!position.IsEmpty(sq)) 
				break;
		}
		else 
			break;
	}

	// Down-right moves
	sqStart = square + DELTA_SE;
	sqEnd = (Square)max(-1, square - 7 * (8 - col));
	for (Square sq = sqStart; sq > sqEnd ; sq += DELTA_SE)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleBishopMoves.push_back(make_move(square, sq));
			if (!position.IsEmpty(sq)) 
				break;
		}
		else 
			break;
	}

	// Down-left moves
	sqStart = square + DELTA_SW;
	sqEnd = (Square)max(-1, square - 9 * (col + 1));
	for (Square sq = sqStart ; sq > sqEnd; sq += DELTA_SW)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleBishopMoves.push_back(make_move(square, sq));
			if (!position.IsEmpty(sq))
				break;
		}
		else 
			break;
	}
	// Up-left moves
	sqStart = square + DELTA_NW;
	sqEnd = (Square)min(64, square + 7 * (col + 1));
	for (Square sq = sqStart; sq < sqEnd; sq += DELTA_NW)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleBishopMoves.push_back(make_move(square, sq));
			if (!position.IsEmpty(sq))
				break;
		}
		else
			break;
	}
}

void Engine::GetPossibleBishopCaptures(Position& position, Square square, vector<Move> & possibleBishopMoves)
{
	//vector<Move> possibleBishopMoves = new vector<Move>();
	File col = file_of(square);
	Rank row = rank_of(square);

	// Up-right moves
	Square sqStart = square + DELTA_NE;
	Square sqEnd = (Square)min(64, square + 9 * (8 - col));
	for (Square sq = sqStart; sq < sqEnd; sq += DELTA_NE)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleBishopMoves.push_back(make_move(square, sq));
		}
		else
			if (!position.IsEmpty(sq)) 
				break;
	}

	// Down-right moves
	sqStart = square + DELTA_SE;
	sqEnd = (Square)max(-1, square - 7 * (8 - col));
	for (Square sq = sqStart; sq > sqEnd ; sq += DELTA_SE)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleBishopMoves.push_back(make_move(square, sq));
		}
		else
			if (!position.IsEmpty(sq)) 
				break;
	}

	// Down-left moves
	sqStart = square + DELTA_SW;
	sqEnd = (Square)max(-1, square - 9 * (col + 1));
	for (Square sq = sqStart ; sq > sqEnd; sq += DELTA_SW)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleBishopMoves.push_back(make_move(square, sq));
		}
		else
			if (!position.IsEmpty(sq))
				break;
	}
	// Up-left moves
	sqStart = square + DELTA_NW;
	sqEnd = (Square)min(64, square + 7 * (col + 1));
	for (Square sq = sqStart; sq < sqEnd; sq += DELTA_NW)
	{
		if (position.IsEmptyOrOpponent(sq))
		{
			//Move move = make_move(square, sq);
			possibleBishopMoves.push_back(make_move(square, sq));
		}
		else
			if (!position.IsEmpty(sq))
				break;
	}
}

/**
* Get possible moves for Pawn
* 
* @param currentPosition
* @param square
* @return
*/
void Engine::GetPossiblePawnMoves(Position& position, Square square, vector<Move> & possiblePawnMoves)
{
	vector<Move> pawnMoves;// = new vector<Move>();
	Piece piece = position.GetPiece(square);
	File col = file_of(square);
	Rank row = rank_of(square);

	if (piece == W_PAWN && row < RANK_8)
	{
		// Pawn's move
		//if (position.GetPiece(SquareGet(col, row + 1)) == EMPTY)
		if (position.GetPiece(square+DELTA_N) == NO_PIECE)
		{
			//Move move = make_move(square, SquareGet(col, row + 1));
			//Move move = make_move(square, square + DELTA_N);
			pawnMoves.push_back(make_move(square, square + DELTA_N));
		}
		//if (row == 1 && (position.GetPiece(SquareGet(col, row + 1)) == EMPTY) && (position.GetPiece(SquareGet(col, row + 2)) == EMPTY))
		if (row == RANK_2 && (position.GetPiece(square+DELTA_N) == NO_PIECE) && (position.GetPiece(square+DELTA_NN) == NO_PIECE))
		{
			//Move move = make_move(square, SquareGet(col, row + 2));
			//Move move = make_move(square, square + DELTA_NN);
			pawnMoves.push_back(make_move(square, square + DELTA_NN));
		}
		// Pawn capture
		//if (col < FILE_H && position.IsPieceBlack(position.GetPiece(SquareGet(col + 1, row + 1))))
		if (col < FILE_H && (position.IsPieceBlack(position.GetPiece(square+DELTA_NE)) || (position.gameState.lastEnPassant == (square+DELTA_NE))))
		{
			//Move move = make_move(square, SquareGet(col + 1, row + 1));
			//Move move = make_move(square, square + DELTA_NE);
			pawnMoves.push_back(make_move(square, square + DELTA_NE));
		}
		//if (col > FILE_A && position.IsPieceBlack(position.GetPiece(SquareGet(col - 1, row + 1))))
		if (col > FILE_A && (position.IsPieceBlack(position.GetPiece(square+DELTA_NW))|| (position.gameState.lastEnPassant == (square+DELTA_NW))))
		{
			//Move move = make_move(square, SquareGet(col - 1, row + 1));
			//Move move = make_move(square, square + DELTA_NW);
			pawnMoves.push_back(make_move(square, square + DELTA_NW));
		}
		// TODO implement "en passant"

	}
	if (piece == B_PAWN && row > RANK_1)
	{
		// Pawn's move
		//if (position.GetPiece(SquareGet(col, row - 1)) == EMPTY)
		if (position.GetPiece(square+DELTA_S) == NO_PIECE)
		{
			//Move move = make_move(square, SquareGet(col, row - 1));
			//Move move = make_move(square, square + DELTA_S);
			pawnMoves.push_back(make_move(square, square + DELTA_S));
		}
		//if (row == 6 && (position.GetPiece(SquareGet(col, row - 1)) == EMPTY) && (position.GetPiece(SquareGet(col, row - 2)) == EMPTY))
		if (row == RANK_7 && (position.GetPiece(square + DELTA_S) == NO_PIECE) && (position.GetPiece(square + DELTA_SS) == NO_PIECE))

		{
			//Move move = make_move(square, SquareGet(col, row - 2));
			//Move move = make_move(square, square + DELTA_SS);
			pawnMoves.push_back(make_move(square, square + DELTA_SS));

		}
		// Pawn capture
		// Pawn capture
		if (col < FILE_H && (position.IsPieceWhite(position.GetPiece(square + DELTA_SE)) || (position.gameState.lastEnPassant == (square + DELTA_SE))))
		{
			//Move move = make_move(square, square + DELTA_SE);
			pawnMoves.push_back(make_move(square, square + DELTA_SE));
		}
		if (col > FILE_A && (position.IsPieceWhite(position.GetPiece(square + DELTA_SW)) || (position.gameState.lastEnPassant == (square + DELTA_SW))))
		{
			//Move move = make_move(square, square + DELTA_SW);
			pawnMoves.push_back(make_move(square, square + DELTA_SW));
		}
	}

	// adding alternate promotions
	//foreach (Move m in pawnMoves)
	for(vector<Move>::iterator m = pawnMoves.begin(); m != pawnMoves.end(); m++)
		//for(int i=0;i<pawnMoves.size();i++)
	{
		//Move m = pawnMoves.get(i);

		Rank r = rank_of(to_sq(*m));
		if ((r == RANK_1) || (r == RANK_8))
		{
			possiblePawnMoves.push_back(make_promotion(from_sq(*m), to_sq(*m), QUEEN));
			possiblePawnMoves.push_back(make_promotion(from_sq(*m), to_sq(*m), ROOK));
			possiblePawnMoves.push_back(make_promotion(from_sq(*m), to_sq(*m), BISHOP));
			possiblePawnMoves.push_back(make_promotion(from_sq(*m), to_sq(*m), KNIGHT));
		}
		else
		{
			possiblePawnMoves.push_back(*m);
		}
	}

}

void Engine::GetPossiblePawnCaptures(Position& position, Square square, vector<Move> & possiblePawnMoves)
{
	vector<Move> pawnMoves;// = new vector<Move>();
	Piece piece = position.GetPiece(square);
	File col = file_of(square);
	Rank row = rank_of(square);

	if (piece == W_PAWN && row < RANK_8)
	{
		// Pawn capture
		if (col < FILE_H && (position.IsPieceBlack(position.GetPiece(square+DELTA_NE)) || (position.gameState.lastEnPassant == (square+DELTA_NE))))
		{
			pawnMoves.push_back(make_move(square, square + DELTA_NE));
		}
		if (col > FILE_A && (position.IsPieceBlack(position.GetPiece(square+DELTA_NW))|| (position.gameState.lastEnPassant == (square+DELTA_NW))))
		{
			pawnMoves.push_back(make_move(square, square + DELTA_NW));
		}
	}
	if (piece == B_PAWN && row > RANK_1)
	{
		// Pawn capture
		if (col < FILE_H && (position.IsPieceWhite(position.GetPiece(square + DELTA_SE)) || (position.gameState.lastEnPassant == (square + DELTA_SE))))
		{
			pawnMoves.push_back(make_move(square, square + DELTA_SE));
		}
		if (col > FILE_A && (position.IsPieceWhite(position.GetPiece(square + DELTA_SW)) || (position.gameState.lastEnPassant == (square + DELTA_SW))))
		{
			pawnMoves.push_back(make_move(square, square + DELTA_SW));
		}
	}

	// adding alternate promotions
	//foreach (Move m in pawnMoves)
	for(vector<Move>::iterator m = pawnMoves.begin(); m != pawnMoves.end(); m++)
		//for(int i=0;i<pawnMoves.size();i++)
	{
		//Move m = pawnMoves.get(i);

		Rank r = rank_of(to_sq(*m));
		if ((r == RANK_1) || (r == RANK_8))
		{
			possiblePawnMoves.push_back(make_promotion(from_sq(*m), to_sq(*m), QUEEN));
			possiblePawnMoves.push_back(make_promotion(from_sq(*m), to_sq(*m), ROOK));
			possiblePawnMoves.push_back(make_promotion(from_sq(*m), to_sq(*m), BISHOP));
			possiblePawnMoves.push_back(make_promotion(from_sq(*m), to_sq(*m), KNIGHT));
		}
		else
		{
			possiblePawnMoves.push_back(*m);
		}
	}

}

/**
* Get legal moves - Main bottleneck
* @param currentPosition
* @return
*/
int Engine::GetLegalMoves(Position & pos, vector<Move> & legalMoves)
{
	legalMoves.clear();
	if (IsFiftyMovesDraw(pos) /*|| IsThreeTimesRepetitionDraw(pos)*/)
		return 0;

	// 1- retrieve all moves
	GetPossibleMoves(pos, legalMoves);

	// 2 - prune illegal moves
	// copy elements and drop the rest of the list
	// no temporary vector 
	Color turn = pos.GetTurn();
	int lastLegalMoveIndex = 0;
	for( unsigned int i=0 ; i < legalMoves.size() ; i++ )
	{
		Move m = legalMoves[i];
		pos.Play(m);
		// new bitboard implementation
		if(!pos.IsCheck(turn))
		{
			if(i != lastLegalMoveIndex) // do the copy only if needed
				legalMoves[lastLegalMoveIndex] = m;
			lastLegalMoveIndex++;		// the move is legal, always increment
		}
		pos.Unplay(m);
	}
	// resize the list
	legalMoves.resize(lastLegalMoveIndex); // WRONG USAGE: reduce the container size
	//}

	// Sort moves
	Sort(currentPosition.board, legalMoves);

	return legalMoves.size();
}

int Engine::GetLegalCaptures(Position & pos, vector<Move> & legalCaptures)
{
	legalCaptures.clear();
	if (IsFiftyMovesDraw(pos) /*|| IsThreeTimesRepetitionDraw(pos)*/)
		return 0;

	// 1- retrieve all moves
	GetPossibleCaptures(pos, legalCaptures);

	// 2 - prune illegal moves
	// copy elements and drop the rest of the list
	// no temporary vector 
	Color turn = pos.GetTurn();
	int lastLegalMoveIndex = 0;
	for( unsigned int i=0 ; i < legalCaptures.size() ; i++ )
	{
		Move m = legalCaptures[i];
		pos.Play(m);
		// new bitboard implementation
		if(!pos.IsCheck(turn))
		{
			if(i != lastLegalMoveIndex) // do the copy only if needed
				legalCaptures[lastLegalMoveIndex] = m;
			lastLegalMoveIndex++;		// the move is legal, always increment
		}
		pos.Unplay(m);
	}
	// resize the list
	legalCaptures.resize(lastLegalMoveIndex); // WRONG USAGE: reduce the container size
	//}

	// Sort moves
	Sort(currentPosition.board, legalCaptures);

	return legalCaptures.size();
}

// time consuming & not mandatory
//bool Engine::IsThreeTimesRepetitionDraw(Position& pos)
//{
//	// the position cannot be identical if the 50 moves clock is lower than 6 half plies
//	if (pos.GetMovesWithoutCaptureOrPawnPush() < 6 
//		|| pos.GetPlayedMoveNumber() < 6)
//		return false;
//
//	return pos.IsThreeTimesRepetitionDraw();
//}




Move Engine::GetBestMove()
{
	return bestMove;
}


void Engine::SetDepthLevel(int depth)
{
	this->depthLevel = depth;
}

void Engine::SetTimeLevel(int timeLevel)
{
	brainMode = MOVETIME_MODE;
	this->thinkDelay = timeLevel;
}

void Engine::SetStartPosition()
{
	SetPosition("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
}

void Engine::SetPosition(string strPos)
{
	//currentPosition = new Position(strPos);
	currentPosition.SetFromFEN(strPos);
}

void Engine::Ponderhit()
{
	////throw new NotImplementedException();
}

void Engine::SetSearchmovesMode(vector<Move> & selectedMoves)
{
	// don't forget to switch to false once used
	this->moves = selectedMoves;
	isSearchMove = true;
}

void Engine::SetPonderMode()
{
	////throw new NotImplementedException();
}

void Engine::SetWtime(int timeLeft)
{
	this->wtime = timeLeft;
	this->brainMode = ADJUSTTIME_MODE;
}

void Engine::SetBtime(int timeLeft)
{
	this->btime = timeLeft;
	this->brainMode = ADJUSTTIME_MODE;
}

void Engine::SetWinc(int increment)
{
	this->winc = increment;
	this->brainMode = ADJUSTTIME_MODE;
}

void Engine::SetBinc(int increment)
{
	this->binc = increment;
	this->brainMode = ADJUSTTIME_MODE;
}

void Engine::SetMovestogo(int movesUntilNextTimeControl)
{
	//throw new NotImplementedException();
}

void Engine::SetDepthMode(int pliesDepth)
{
	brainMode = DEPTH_MODE;
	depthLevel = pliesDepth; 
}

void Engine::SetNodesMode(int nodesDepth)
{
	brainMode = NODES_MODE;
	this->nodeMaxNumber = nodesDepth;
}

void Engine::SetMateMode(int mateDepth)
{
	brainMode = MATE_MODE;
	depthLevel = mateDepth;
}

void Engine::SetMovetimeMode(int searchTime)
{
	this->thinkDelay = searchTime;
	brainMode = MOVETIME_MODE;
}

void Engine::SetInfiniteMode()
{
	brainMode = INFINITE_MODE;
}

void Engine::SetVerboseMode(bool isVerboseMode)
{
	isVerbose = isVerboseMode;
}

void Engine::UpdateThinkDelay()
{
	int playerInc = 0;
	int playerTime = 0;
	int oppInc = 0;
	int oppTime = 0;
	if (this->GetTurn() == WHITE)
	{
		playerInc = winc;
		playerTime = wtime;
		oppInc = binc;
		oppTime = btime;
	} else {
		playerInc = binc;
		playerTime = btime;
		oppInc = winc;
		oppTime = wtime;
	}
	int delay;
	int extraDelay;
	// Smart timing : the engine adjust its think time depending on 
	// the time left for itself and the opponent
	// TODO: improve calculation
	// TODO: link calculation to current nodeStack/score state
	if (isSmartTiming)
	{
		if (playerInc == 0)
		{
			// TODO: improve time calculation when there is no inc

			// no increment
			// time must be managed carefully
			if (this->GetTurn() == WHITE)
			{
				// White to play
				if (playerTime > oppTime)
				{
					delay = (int)max(playerInc, playerTime / 20);
				}
				else
				{
					delay = (int)max(playerInc, playerTime / 20);
				}


			}
			else
			{
				// Black to play
				// they can decide how much time to spend depending on whites clock
				if (playerTime > oppTime)
				{
					delay = (int)max(playerInc, playerTime / 20);
				}
				else
				{
					delay = (int)max(playerInc, playerTime / 20);
				}

			}


		}
		else
		{
			// base time
			delay = playerInc;

			// extra time taken from the clock
			if(playerTime<10000){
				// for timer < 10s try to save some time
				extraDelay = playerTime*60/100;
			}
			else{
				extraDelay = max(playerTime/min(40,max(5,abs(40-currentPosition.GetPlayedMoveNumber()))),8000);
			}
			delay += extraDelay;
		}

	} 
	else
		// NO SMART TIMING
	{
		delay = (int)max(playerInc, playerTime / 20);
	}


	thinkDelay = delay;
	cout << "Think delay updated : " << thinkDelay << endl;
}


void Engine::DisplayBestPv(int maxDepth, long nodeNb, long time, Move* pv) {

	//if (possibleNodes.size() > 0) {
	//SortLegalMoves();

	int depth = 1;
	//Node bestNode = GetBestNode();
	//cout << "info currmove " + bestNode.GetPreviousMove());

	//long time = watch.ElapsedMilliseconds;
	//int nodeNb = nodeQueue.size();//+hashtable.size();
	long nps = (time == 0) ? 0 : (1000 * nodeNb / time);
	//int hashfull = (hashMaxSize == 0) ? 0 : (1000 * hashtable.size() / hashMaxSize);
	ostringstream pvs;

	pvs << "info nodes " << nodeNb 
		<< " nps " << nps 
		<< " depth " << maxDepth 
		<< " score " << ScoreToString(bestScore)
		<< " time " << time
		<< " pv ";
	
	cout << pvs.str() ;
	DisplayPv(pv);

	cout << endl;

}

void Engine::DisplayPv(Move* pv){
	int i=0;
	Move m = pv[i++];
	while(m != MOVE_NULL){
		cout << move_to_string(m) << ' ';
		m = pv[i++];
	}
}




void Engine::SetPosition(Position& pos)
{
	currentPosition = pos;
}

string Engine::ScoreToString(int score)
{
	char* scoreStr = new char[8];
	if (score < -(VALUE_MATE / 2) || score > (VALUE_MATE / 2))
	{
		//string mateIn = "mate ";
		int plies = 0;
		if (score > 0)
		{
			plies = (VALUE_MATE - score);
		}
		else
		{
			plies = (-VALUE_MATE - score);
		}
		//mateIn = mateIn + plies;
		sprintf(scoreStr,"mate %d", plies);
		//return mateIn;
	}
	else
	{
		sprintf(scoreStr,"cp %d", score);
		//return "cp " + score;
	}
	return scoreStr;
}

Position& Engine::GetCurrentPosition()
{
	return currentPosition;
}

int Engine::GetBestScore()
{
	return bestScore;
}

string Engine::GetEngineInfo(){
	const string str = "OliveChess 0.2.3 - Olivier HERAU, France\n";
	/*
	"- C# to C++ conversion\n"
	"- several search algorithms available\n"
	"- using bitboards\n"
	"- optimized IsCheck function\n"
	"- new commands 'test' and 'bench'"
	"- using bitboards";*/
	return str;
}

string Engine::GetEngineOptions(){
	// TODO: manage a map of options
	const string str = "";
	return str;
}

